This tutorial will demonstrate how to analyse audio data for acoustic phonetic studies in R. It is mainly intended to demonstrated possible workflows. The topics covered are:

Preamble

This tutorial is organised as an R Markdown notebook. When you execute code within the notebook, the results appear beneath the code. In order to do so, you need to have R and RStudio installed. When this R notebook is loaded into RStudio, you can excecute chunks by clicking the Run button within the chunk or by placing your cursor inside it and pressing Cmd+Shift+Enter, allowing you to experiment with the code. This handout contains all output of the code (tables, visualisations etc.).

The following libraries will be needed and have to be installed.

library(tidyverse)
── Attaching packages ──────────────────────────────────── tidyverse 1.3.0 ──
✓ ggplot2 3.3.0           ✓ purrr   0.3.3      
✓ tibble  3.0.0           ✓ dplyr   0.8.99.9002
✓ tidyr   1.0.2           ✓ stringr 1.4.0      
✓ readr   1.3.1           ✓ forcats 0.5.0      
Warning: package 'tibble' was built under R version 3.6.2
── Conflicts ─────────────────────────────────────── tidyverse_conflicts() ──
x dplyr::filter() masks stats::filter()
x dplyr::lag()    masks stats::lag()
library(ggiraph)
library(cowplot)

********************************************************
Note: As of version 1.0.0, cowplot does not change the
  default ggplot2 theme anymore. To recover the previous
  behavior, execute:
  theme_set(theme_cowplot())
********************************************************
library(emuR)

Attaching package: 'emuR'
The following object is masked from 'package:base':

    norm
library(tools)
library(rio)
library(ggplot2)
library(magrittr)

Attaching package: 'magrittr'
The following object is masked from 'package:purrr':

    set_names
The following object is masked from 'package:tidyr':

    extract
library(ggiraph)
library(htmltools)
library(shiny)
library(joeyr, warn.conflicts = FALSE )
This is the "joeyr" package.
library(knitr)

1 The LOD database

The database contains the audio recordings from the Lëtzebuerger Online Dictionnaire (available here, spoken by one female speaker. The audio files have been automatically segmented with the MAUS tools. We thus have a database conisting of textual data, basically words, and the corresponding audio data. The audio data is segmented into words and phonetic segments (sounds).

This database has been created beforehand. Infos how to create such a database is explained in the EMU-SDMS manual.

We start with loading the database and give an overview of structure and content.

# load database
#db = load_emuDB("/Users/peter.gilles/Documents/_Daten/LOD-emuDB/lod_emuDB")
db = load_emuDB("lod_emuDB")
INFO: Checking if cache needs update for 1 sessions and 539 bundles ...
INFO: Performing precheck and calculating checksums (== MD5 sums) for _annot.json files ...
INFO: Nothing to update!
# display the overview of the structure and content
summary(db)
Name:    lod 
UUID:    c87793c0-012a-11e9-874b-68b599b5deb4 
Directory:   /Users/peter.gilles/Documents/Data_Science_Humanities/lod_emuDB 
Session count: 1 
Bundle count: 539 
Annotation item count:  10979 
Label count:  13547 
Link count:  10440 

Database configuration:

SSFF track definitions:
      name columnName fileExtension
1      dft        dft           dft
2 praatFms         fm      praatFms

Level definitions:
    name    type nrOfAttrDefs              attrDefNames
1 bundle    ITEM            4 bundle; source; SAM; MAO;
2    ORT    ITEM            2                 ORT; KAN;
3    MAU SEGMENT            1                      MAU;

Link definitions:
         type superlevelName sublevelName
1 ONE_TO_MANY         bundle          ORT
2 ONE_TO_MANY            ORT          MAU

Tracks in emuR are acoustic representation of the speech signal, here dft for the waveform (time-amplitude representation) and praatFms for the formant measures of vowels (see below).

Levels in an eumR database stand for level of interlinked linguistic information. bundle is the entire audio file, ORT stands for the orthographical representation of the audio file segmented in its single words. MAU is the segmentation of all phonetic segment (=sounds) of all ORT segments in bundle.

The hierarchical structure of these levels is expressed in the link definitions as ONE-TO-MANY.

2 Database queries

An emuR database can be queried with a powerful query engine. The first example is a simple query for one word, Aarbecht.

sl = query(db, query = "[ ORT == 'Aarbecht']")
sl

The result is a segment list (sl), containing various information about the found item (time, level, name, database info). The result of the query can also be displayed in the EMU Speech Database Management System.

serve(db, seglist = sl)

The GUI will open in the Viewer pane of RStudio or you can open it in a browser (Chrome preferred).

Here we can also display the hiearchical structure for this database item, which is accessed during queries. bundle is the top-level, representing the entire audio file.

The ORT level contains the nodes for the individual words in the bundle, here the two words Aarbecht and Aarbechten. The dependend level then is MAU (=Munich Automatic Unit) representing the single sounds of the words in ORT.

Two aspects render emuR query system extremly powerful: the use of regular expressions (including negation and other extensions) and the combinated query on different levels of the database.

Let’s try more complex queries:

sl = query(db, query = "[ ORT =~ 'Aarbecht.*']")
sl

Select the vowel [aː] in all words beginning with Aarbecht… Note that in the segment list the label now has changed to the vowel and the respective start-end information is now only for this sound [aː].

sl = query(db, query = "[ ORT =~ 'Aarbecht.*' ^ #MAU=='aː']")
sl
sl = query(db, query = "[ #ORT =~'.*' ^ MAU !~ '[nmɑaːətd]' ]")
sl

With the query the user can compile the data frame from the database which then forms the subset for the phonetic analysis. We can select e.g. all instances of certain (or all) vowels, specifying the context before or after etc. etc.

Of course, querying for individual segments in the audio file like words or sounds is possible only, if this information has been added to the database before.

3 Vowel explorer

knitr::include_app("https://petergill.shinyapps.io/shinyplay/")

4 Calculate the Pillai distance

LS0tCnN1YnRpdGxlOiAiTGVjdHVyZSBmb3IgY2xhc3M6IERhdGEgc2NpZW5jZSBpbiB0aGUgSHVtYW5pdGllcyIKdGl0bGU6ICJCaWcgZGF0YSBpbiB0aGUgYWNvdXN0aWMgcGhvbmV0aWMgYW5hbHlzaXMiCmtuaXQ6IChmdW5jdGlvbihpbnB1dF9maWxlLCBlbmNvZGluZykgewogIG91dF9kaXIgPC0gJ2RvY3MnOwogIHJtYXJrZG93bjo6cmVuZGVyKGlucHV0X2ZpbGUsCiBlbmNvZGluZz1lbmNvZGluZywKIG91dHB1dF9maWxlPWZpbGUucGF0aChkaXJuYW1lKGlucHV0X2ZpbGUpLCBvdXRfZGlyLCAnaW5kZXguaHRtbCcpKX0pCiAKYXV0aG9yOiAiUGV0ZXIgR2lsbGVzIgpkYXRlOiAiMjkuIEFwcmlsIDIwMjAsIDE0aDAwIC0gMTZoMDAsIFVuaXZlcnNpdHkgb2YgTHV4ZW1ib3VyZyIKb3V0cHV0OgogICN0dWZ0ZTo6dHVmdGVfaHRtbDoKICAjdHVmdGU6OnR1ZnRlX2hhbmRvdXQ6IAogIGh0bWxfbm90ZWJvb2s6IAogICAgdG9jOiB0cnVlCiAgICB0b2NfZGVwdGg6IDIKICAgIG51bWJlcl9zZWN0aW9uczogdHJ1ZQotLS0KClRoaXMgdHV0b3JpYWwgd2lsbCBkZW1vbnN0cmF0ZSBob3cgdG8gYW5hbHlzZSBhdWRpbyBkYXRhIGZvciBhY291c3RpYyBwaG9uZXRpYyBzdHVkaWVzIGluIFIuIEl0IGlzIG1haW5seSBpbnRlbmRlZCB0byBkZW1vbnN0cmF0ZWQgcG9zc2libGUgd29ya2Zsb3dzLiBUaGUgdG9waWNzIGNvdmVyZWQgYXJlOgoKKiBwaG9uZXRpYyBkYXRhYmFzZXMsIHRoZSBjYXNlIG9mIGBlbXVSYCBhbmQgYEVNVS1TRE1TYCwgdGhlIEVNVSBTcGVlY2ggRGF0YWJhc2UgTWFuYWdlbWVudCBTeXN0ZW0KKiBTYW1wbGUgZGF0YTogdGhlIExPRCBkYXRhYmFzZQoqIFF1ZXJpZXMgYW5kIHJlcXVlcmllcwoqIEluc2NwZWN0IHRoZSBkYXRhYmFzZTogYHNlcnZlKClgCiogQ2FsY3VsYXRlIGR1cmF0aW9uIGZvciB2b3dlbCBjYXRlZ29yaWVzCiogVm93ZWxzIGZvcm1hbnRzIGFuZCB2aXN1YWxpc2F0aW9ucyB3aXRoIGBnZ3Bsb3QyYAoqIFZvd2VsIGV4cGxvcmVyCiogQ2FsY3VsYXRpbmcgdGhlIFBpbGxhaSBkaXN0YW5jZQoKIyBQcmVhbWJsZSB7LX0KClRoaXMgdHV0b3JpYWwgaXMgb3JnYW5pc2VkIGFzICBhbiBbUiBNYXJrZG93bl0oaHR0cDovL3JtYXJrZG93bi5yc3R1ZGlvLmNvbSkgbm90ZWJvb2suIFdoZW4geW91IGV4ZWN1dGUgY29kZSB3aXRoaW4gdGhlIG5vdGVib29rLCB0aGUgcmVzdWx0cyBhcHBlYXIgYmVuZWF0aCB0aGUgY29kZS4gSW4gb3JkZXIgdG8gZG8gc28sIHlvdSBuZWVkIHRvIGhhdmUgUiBhbmQgUlN0dWRpbyBpbnN0YWxsZWQuIFdoZW4gdGhpcyBSIG5vdGVib29rIGlzIGxvYWRlZCBpbnRvIFJTdHVkaW8sIHlvdSBjYW4gZXhjZWN1dGUgY2h1bmtzIGJ5IGNsaWNraW5nIHRoZSAqUnVuKiBidXR0b24gd2l0aGluIHRoZSBjaHVuayBvciBieSBwbGFjaW5nIHlvdXIgY3Vyc29yIGluc2lkZSBpdCBhbmQgcHJlc3NpbmcgKkNtZCtTaGlmdCtFbnRlciosIGFsbG93aW5nIHlvdSB0byBleHBlcmltZW50IHdpdGggdGhlIGNvZGUuIFRoaXMgaGFuZG91dCBjb250YWlucyBhbGwgb3V0cHV0IG9mIHRoZSBjb2RlICh0YWJsZXMsIHZpc3VhbGlzYXRpb25zIGV0Yy4pLgoKVGhlIGZvbGxvd2luZyBsaWJyYXJpZXMgd2lsbCBiZSBuZWVkZWQgYW5kIGhhdmUgdG8gYmUgaW5zdGFsbGVkLgpgYGB7cn0KbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoZ2dpcmFwaCkKbGlicmFyeShjb3dwbG90KQpsaWJyYXJ5KGVtdVIpCmxpYnJhcnkodG9vbHMpCmxpYnJhcnkocmlvKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkobWFncml0dHIpCmxpYnJhcnkoZ2dpcmFwaCkKbGlicmFyeShodG1sdG9vbHMpCmxpYnJhcnkoc2hpbnkpCmxpYnJhcnkoam9leXIsIHdhcm4uY29uZmxpY3RzID0gRkFMU0UgKQpsaWJyYXJ5KGtuaXRyKQpgYGAKCgojIFRoZSBMT0QgZGF0YWJhc2UKClRoZSBkYXRhYmFzZSBjb250YWlucyB0aGUgYXVkaW8gcmVjb3JkaW5ncyBmcm9tIHRoZSBgTMOrdHplYnVlcmdlciBPbmxpbmUgRGljdGlvbm5haXJlYCAoYXZhaWxhYmxlIFtoZXJlXShodHRwczovL2dpdGh1Yi5jb20vc3BlbGxjaGVja2VyLWx1KSwgc3Bva2VuIGJ5IG9uZSBmZW1hbGUgc3BlYWtlci4gVGhlIGF1ZGlvIGZpbGVzIGhhdmUgYmVlbiBhdXRvbWF0aWNhbGx5IHNlZ21lbnRlZCB3aXRoIHRoZSBbTUFVUyB0b29sc10oaHR0cHM6Ly9jbGFyaW4ucGhvbmV0aWsudW5pLW11ZW5jaGVuLmRlL0JBU1dlYlNlcnZpY2VzL2ludGVyZmFjZSkuIFdlIHRodXMgaGF2ZSBhIGRhdGFiYXNlIGNvbmlzdGluZyBvZiB0ZXh0dWFsIGRhdGEsIGJhc2ljYWxseSB3b3JkcywgYW5kIHRoZSBjb3JyZXNwb25kaW5nIGF1ZGlvIGRhdGEuIFRoZSBhdWRpbyBkYXRhIGlzIHNlZ21lbnRlZCBpbnRvIHdvcmRzIGFuZCBwaG9uZXRpYyBzZWdtZW50cyAoc291bmRzKS4KClRoaXMgZGF0YWJhc2UgaGFzIGJlZW4gY3JlYXRlZCBiZWZvcmVoYW5kLiBJbmZvcyBob3cgdG8gY3JlYXRlIHN1Y2ggYSBkYXRhYmFzZSBpcyBleHBsYWluZWQgaW4gdGhlIFtFTVUtU0RNUyBtYW51YWxdKGh0dHBzOi8vaXBzLWxtdS5naXRodWIuaW8vVGhlLUVNVS1TRE1TLU1hbnVhbC8pLgoKV2Ugc3RhcnQgd2l0aCBsb2FkaW5nIHRoZSBkYXRhYmFzZSBhbmQgZ2l2ZSBhbiBvdmVydmlldyBvZiBzdHJ1Y3R1cmUgYW5kIGNvbnRlbnQuCmBgYHtyfQojIGxvYWQgZGF0YWJhc2UKI2RiID0gbG9hZF9lbXVEQigiL1VzZXJzL3BldGVyLmdpbGxlcy9Eb2N1bWVudHMvX0RhdGVuL0xPRC1lbXVEQi9sb2RfZW11REIiKQpkYiA9IGxvYWRfZW11REIoImxvZF9lbXVEQiIpCgojIGRpc3BsYXkgdGhlIG92ZXJ2aWV3IG9mIHRoZSBzdHJ1Y3R1cmUgYW5kIGNvbnRlbnQKc3VtbWFyeShkYikKCmBgYAoKVHJhY2tzIGluIGBlbXVSYCBhcmUgYWNvdXN0aWMgcmVwcmVzZW50YXRpb24gb2YgdGhlIHNwZWVjaCBzaWduYWwsIGhlcmUgYGRmdGAgZm9yIHRoZSB3YXZlZm9ybSAodGltZS1hbXBsaXR1ZGUgcmVwcmVzZW50YXRpb24pIGFuZCBgcHJhYXRGbXNgIGZvciB0aGUgZm9ybWFudCBtZWFzdXJlcyBvZiB2b3dlbHMgKHNlZSBiZWxvdykuIAoKTGV2ZWxzIGluIGFuIGBldW1SYCBkYXRhYmFzZSBzdGFuZCBmb3IgbGV2ZWwgb2YgaW50ZXJsaW5rZWQgbGluZ3Vpc3RpYyBpbmZvcm1hdGlvbi4gYGJ1bmRsZWAgaXMgdGhlIGVudGlyZSBhdWRpbyBmaWxlLCBgT1JUYCBzdGFuZHMgZm9yIHRoZSBvcnRob2dyYXBoaWNhbCByZXByZXNlbnRhdGlvbiBvZiB0aGUgYXVkaW8gZmlsZSBzZWdtZW50ZWQgaW4gaXRzIHNpbmdsZSB3b3Jkcy4gYE1BVWAgaXMgdGhlIHNlZ21lbnRhdGlvbiBvZiBhbGwgcGhvbmV0aWMgc2VnbWVudCAoPXNvdW5kcykgb2YgYWxsIGBPUlRgIHNlZ21lbnRzIGluIGBidW5kbGVgLgoKVGhlIGhpZXJhcmNoaWNhbCBzdHJ1Y3R1cmUgb2YgdGhlc2UgbGV2ZWxzIGlzIGV4cHJlc3NlZCBpbiB0aGUgYGxpbmsgZGVmaW5pdGlvbnNgIGFzIGBPTkUtVE8tTUFOWWAuCgoKCiMgRGF0YWJhc2UgcXVlcmllcwoKQW4gZW11UiBkYXRhYmFzZSBjYW4gYmUgcXVlcmllZCB3aXRoIGEgcG93ZXJmdWwgcXVlcnkgZW5naW5lLiBUaGUgZmlyc3QgZXhhbXBsZSBpcyBhIHNpbXBsZSBxdWVyeSBmb3Igb25lIHdvcmQsIGBBYXJiZWNodGAuCgpgYGB7cn0Kc2wgPSBxdWVyeShkYiwgcXVlcnkgPSAiWyBPUlQgPT0gJ0FhcmJlY2h0J10iKQpzbApgYGAKClRoZSByZXN1bHQgaXMgYSBgc2VnbWVudCBsaXN0YCAoYHNsYCksIGNvbnRhaW5pbmcgdmFyaW91cyBpbmZvcm1hdGlvbiBhYm91dCB0aGUgZm91bmQgaXRlbSAodGltZSwgbGV2ZWwsIG5hbWUsIGRhdGFiYXNlIGluZm8pLiBUaGUgcmVzdWx0IG9mIHRoZSBxdWVyeSBjYW4gYWxzbyBiZSBkaXNwbGF5ZWQgaW4gdGhlIEVNVSBTcGVlY2ggRGF0YWJhc2UgTWFuYWdlbWVudCBTeXN0ZW0uIAoKYHNlcnZlKGRiLCBzZWdsaXN0ID0gc2wpYAoKVGhlIEdVSSB3aWxsIG9wZW4gaW4gdGhlIGBWaWV3ZXJgIHBhbmUgb2YgUlN0dWRpbyBvciB5b3UgY2FuIG9wZW4gaXQgaW4gYSBicm93c2VyIChDaHJvbWUgcHJlZmVycmVkKS4KCmBgYHtyIGVjaG89RkFMU0V9CmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKHJlcCgiZW11LXNkbXMucG5nIikpCmBgYAoKSGVyZSB3ZSBjYW4gYWxzbyBkaXNwbGF5IHRoZSBoaWVhcmNoaWNhbCBzdHJ1Y3R1cmUgZm9yIHRoaXMgZGF0YWJhc2UgaXRlbSwgd2hpY2ggaXMgYWNjZXNzZWQgZHVyaW5nIHF1ZXJpZXMuIGBidW5kbGVgIGlzIHRoZSB0b3AtbGV2ZWwsIHJlcHJlc2VudGluZyB0aGUgZW50aXJlIGF1ZGlvIGZpbGUuIAoKVGhlIGBPUlRgIGxldmVsIGNvbnRhaW5zIHRoZSBub2RlcyBmb3IgdGhlIGluZGl2aWR1YWwgd29yZHMgaW4gdGhlIGBidW5kbGVgLCBoZXJlIHRoZSB0d28gd29yZHMgYEFhcmJlY2h0YCBhbmQgYEFhcmJlY2h0ZW5gLiBUaGUgZGVwZW5kZW5kIGxldmVsIHRoZW4gaXMgYE1BVWAgKD1gTXVuaWNoIEF1dG9tYXRpYyBVbml0YCkgcmVwcmVzZW50aW5nIHRoZSBzaW5nbGUgc291bmRzIG9mIHRoZSB3b3JkcyBpbiBgT1JUYC4gCgpgYGB7ciBlY2hvPUZBTFNFfQprbml0cjo6aW5jbHVkZV9ncmFwaGljcyhyZXAoImhpZXJhcmNoeS5wbmciKSkKYGBgCgpUd28gYXNwZWN0cyByZW5kZXIgZW11UiBxdWVyeSBzeXN0ZW0gZXh0cmVtbHkgcG93ZXJmdWw6IHRoZSB1c2Ugb2YgcmVndWxhciBleHByZXNzaW9ucyAoaW5jbHVkaW5nIG5lZ2F0aW9uIGFuZCBvdGhlciBleHRlbnNpb25zKSBhbmQgdGhlIGNvbWJpbmF0ZWQgcXVlcnkgb24gZGlmZmVyZW50IGxldmVscyBvZiB0aGUgZGF0YWJhc2UuCgpMZXQncyB0cnkgbW9yZSBjb21wbGV4IHF1ZXJpZXM6CgotIHJlZ3VsYXIgZXhwcmVzc2lvbiwgb3BlcmF0b3IgYD1+YCwgd29yZHMgYmVnaW5uaW5nIHdpdGggYEFhcmJlY2h0Li4uYApgYGB7cn0Kc2wgPSBxdWVyeShkYiwgcXVlcnkgPSAiWyBPUlQgPX4gJ0FhcmJlY2h0LionXSIpCnNsCmBgYAoKU2VsZWN0IHRoZSB2b3dlbCBbYcuQXSBpbiBhbGwgd29yZHMgYmVnaW5uaW5nIHdpdGggYEFhcmJlY2h0YC4uLiBOb3RlIHRoYXQgaW4gdGhlIHNlZ21lbnQgbGlzdCB0aGUgbGFiZWwgbm93IGhhcyBjaGFuZ2VkIHRvIHRoZSB2b3dlbCBhbmQgdGhlIHJlc3BlY3RpdmUgc3RhcnQtZW5kIGluZm9ybWF0aW9uIGlzIG5vdyBvbmx5IGZvciB0aGlzIHNvdW5kIFthy5BdLgoKYGBge3J9CnNsID0gcXVlcnkoZGIsIHF1ZXJ5ID0gIlsgT1JUID1+ICdBYXJiZWNodC4qJyBeICNNQVU9PSdhy5AnXSIpCnNsCgpgYGAKCi0gcXVlcnkgYWxsIHdvcmRzIGZyb20gYE9SVGAgd2hlcmUgdGhlIGBNQVVgIGxldmVsIGRvZXMgTk9UIGNvbnRhaW4gdGhlIHNlZ21lbnRzIGBubcmRYcuQyZl0ZGAuCgpgYGB7cn0Kc2wgPSBxdWVyeShkYiwgcXVlcnkgPSAiWyAjT1JUID1+Jy4qJyBeIE1BVSAhfiAnW25tyZFhy5DJmXRkXScgXSIpCnNsCgpgYGAKCldpdGggdGhlIHF1ZXJ5IHRoZSB1c2VyIGNhbiBjb21waWxlIHRoZSBkYXRhIGZyYW1lIGZyb20gdGhlIGRhdGFiYXNlIHdoaWNoIHRoZW4gZm9ybXMgdGhlIHN1YnNldCBmb3IgdGhlIHBob25ldGljIGFuYWx5c2lzLiBXZSBjYW4gc2VsZWN0IGUuZy4gYWxsIGluc3RhbmNlcyBvZiBjZXJ0YWluIChvciBhbGwpIHZvd2Vscywgc3BlY2lmeWluZyB0aGUgY29udGV4dCBiZWZvcmUgb3IgYWZ0ZXIgZXRjLiBldGMuCgpPZiBjb3Vyc2UsIHF1ZXJ5aW5nIGZvciBpbmRpdmlkdWFsIHNlZ21lbnRzIGluIHRoZSBhdWRpbyBmaWxlIGxpa2Ugd29yZHMgb3Igc291bmRzIGlzIHBvc3NpYmxlIG9ubHksIGlmIHRoaXMgaW5mb3JtYXRpb24gaGFzIGJlZW4gYWRkZWQgdG8gdGhlIGRhdGFiYXNlIGJlZm9yZS4KCgojIFZvd2VsIGV4cGxvcmVyCmBgYHtyfQprbml0cjo6aW5jbHVkZV9hcHAoImh0dHBzOi8vcGV0ZXJnaWxsLnNoaW55YXBwcy5pby9zaGlueXBsYXkvIikKYGBgCgojIENhbGN1bGF0ZSB0aGUgUGlsbGFpIGRpc3RhbmNlCgojIyBmcm9tIGh0dHBzOi8vam9leXN0YW5sZXkuY29tL2Jsb2cvYS10dXRvcmlhbC1pbi1jYWxjdWxhdGluZy12b3dlbC1vdmVybGFwCgpgYGB7ciBlY2hvPUZBTFNFfQprbml0cjo6aW5jbHVkZV9ncmFwaGljcyhyZXAoImh0dHBzOi8vam9leXN0YW5sZXkuY29tL2ltYWdlcy9wbG90cy9vdmVybGFwX3R1dG9yaWFsL3BpbGxhaV9leGFtcGxlLnBuZyIpKQoKCmBgYAoKCgo=